package api

import (
	"goadmin/conf"
	"goadmin/dao"
	"goadmin/model"
	"goadmin/utils"
	"time"

	"github.com/gin-gonic/gin"
)

type _sessionIssueReq struct {
	Username  string `json:"username" binding:"required" example:"admin"`
	Password  string `json:"password" binding:"required" example:"123456"`
	CaptchaId string `json:"captchaId" example:""`
	Captcha   string `json:"captcha" example:""`
}

type _sessionIssueRes struct {
	AccessToken  string `json:"accessToken"`
	RefreshToken string `json:"refreshToken"`
	ExpiresIn    string `json:"expiresIn"`
}

type _sessionRefreshReq struct {
	RefreshToken string `json:"refreshToken" binding:"required" example:""`
}

// @Tags session
// @Summary Issue
// @Accept json
// @Produce json
// @Param body body _sessionIssueReq true "Request Body"
// @Success 200 {object} resBody{data=_sessionIssueRes}
// @Router /api/sessions [post]
func SessionIssue(c *gin.Context) {
	var body _sessionIssueReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resInvalidBody(c)
		return
	}

	if conf.App.CaptchaEnable && !utils.VerifyCaptcha(body.CaptchaId, body.Captcha) {
		resFai(c, "Invalid captcha")
		return
	}
	var user model.User
	var err error
	if user, err = dao.GetUserByUsername(body.Username); err != nil {
		resFai(c, "User not found")
		return
	}
	if err := utils.VerifyPwdHash(body.Password, user.Password); err != nil {
		resFai(c, "Incorrect password")
		return
	}
	accessToken, refreshToken, err := utils.GenerateToken(user.Id)
	if err != nil {
		resFai(c, "Failed to generate token")
		return
	}

	dao.SetRefreshToken(user.Id, refreshToken)
	resSuc(c, _sessionIssueRes{
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    time.Now().Add(time.Duration(conf.Jwt.Expire) * time.Second).Format(time.RFC3339),
	})

}

// @Tags session
// @Summary Refresh
// @Accept json
// @Produce json
// @Param body body _sessionRefreshReq true "Request Body"
// @Success 200 {object} resBody{data=_sessionIssueRes}
// @Router /api/sessions [put]
func SessionRefresh(c *gin.Context) {
	var body _sessionRefreshReq
	if err := c.ShouldBindJSON(&body); err != nil {
		resInvalidBody(c)
		return
	}

	userId, err := utils.ParseRefreshToken(body.RefreshToken)
	if err != nil {
		resFai(c, "Invalid refresh token")
		return
	}

	if r, err := dao.GetRefreshToken(userId); err != nil || r != body.RefreshToken {
		resFai(c, "Invalid refresh token")
		return
	}
	if _, err := dao.GetUserById(userId); err != nil {
		resNoAuth(c)
		return
	}
	accessToken, refreshToken, err := utils.GenerateToken(userId)
	if err != nil {
		resFai(c, "Failed to generate token")
		return
	}
	dao.SetRefreshToken(userId, refreshToken)
	resSuc(c, _sessionIssueRes{
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    time.Now().Add(time.Duration(conf.Jwt.Expire) * time.Second).Format(time.RFC3339),
	})
}

// @Tags session
// @Summary Remove
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} resBody
// @Router /api/sessions [delete]
func SessionRemove(c *gin.Context) {
	token := c.GetHeader("Authorization")
	if token != "" {
		userId, err := utils.ParseToken(token)
		if err == nil {
			dao.DelRefreshToken(userId)
		}
	}
	resSuc(c, nil)
}
